package org.truenewx.tnxjee.webmvc.security.web.authentication;

import jakarta.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.truenewx.tnxjee.core.caption.CaptionUtil;
import org.truenewx.tnxjee.core.util.NetUtil;
import org.truenewx.tnxjee.model.spec.user.security.UserConfigAuthority;
import org.truenewx.tnxjee.model.spec.user.security.UserGrantedAuthority;
import org.truenewx.tnxjee.service.exception.BusinessException;
import org.truenewx.tnxjee.service.exception.NoOperationAuthorityException;
import org.truenewx.tnxjee.service.security.access.GrantedAuthorityDecider;
import org.truenewx.tnxjee.service.security.access.RolePermissionsConverter;
import org.truenewx.tnxjee.web.util.WebUtil;
import org.truenewx.tnxjee.webmvc.method.support.HandlerMethodMapping;
import org.truenewx.tnxjee.webmvc.security.authentication.NoOperationAuthorizationDecision;
import org.truenewx.tnxjee.webmvc.security.web.access.WebSecurityMetadataSource;

import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Supplier;

@Component
public class WebAuthorizationManager
        implements AuthorizationManager<RequestAuthorizationContext>, GrantedAuthorityDecider {

    @Autowired
    private HandlerMethodMapping handlerMethodMapping;
    @Autowired
    private WebSecurityMetadataSource securityMetadataSource;
    @Autowired(required = false)
    private RolePermissionsConverter rolePermissionsConverter;

    @Override
    public void verify(Supplier<Authentication> authentication, RequestAuthorizationContext context) {
        AuthorizationDecision decision = check(authentication, context);
        if (decision != null && !decision.isGranted()) {
            if (decision instanceof NoOperationAuthorizationDecision) {
                String operation = ((NoOperationAuthorizationDecision) decision).getOperation();
                BusinessException be = new NoOperationAuthorityException(operation);
                throw new AccessDeniedException(be.getLocalizedMessage(), be);
            }
            throw new AccessDeniedException("Access Denied");
        }
    }

    @Override
    public AuthorizationDecision check(Supplier<Authentication> authentication, RequestAuthorizationContext context) {
        Collection<? extends GrantedAuthority> authorities = authentication.get().getAuthorities();
        HttpServletRequest request = context.getRequest();
        HandlerMethod handlerMethod = this.handlerMethodMapping.getHandlerMethod(request);
        if (handlerMethod != null) {
            Collection<ConfigAttribute> configAttributes = this.securityMetadataSource.getAttributes(handlerMethod);
            if (!contains(request, authorities, configAttributes)) {
                String operation = CaptionUtil.getCaption(handlerMethod.getMethod(), request.getLocale());
                if (operation == null) {
                    operation = WebUtil.getRelativeRequestAction(request);
                }
                return new NoOperationAuthorizationDecision(operation);
            }
        }
        return new AuthorizationDecision(true);
    }


    private boolean contains(HttpServletRequest request, Collection<? extends GrantedAuthority> authorities,
            Collection<ConfigAttribute> configAttributes) {
        boolean granted = true; // 如果没有支持的配置属性限定，则授权默认为通过
        for (ConfigAttribute attribute : configAttributes) {
            if (attribute instanceof UserConfigAuthority) {
                granted = false; // 只要有一个支持的配置属性限定，则授权默认为不通过
                if (contains(request, authorities, (UserConfigAuthority) attribute)) {
                    return true; // 支持的用户配置权限限定有一个通过，则结果视为通过
                }
            }
        }
        return granted;
    }

    private boolean contains(HttpServletRequest request, Collection<? extends GrantedAuthority> authorities,
            UserConfigAuthority configAuthority) {
        if (configAuthority.isDenyAll()) {
            return false;
        }
        if (configAuthority.isIntranet()) { // 如果限制内网访问
            String ip = WebUtil.getRemoteAddress(request);
            if (!NetUtil.isIntranetIp(ip)) {
                return false; // 拒绝非内网访问
            }
        }
        return isGranted(authorities, configAuthority.getType(), configAuthority.getRank(),
                configAuthority.getPermission());
    }

    @Override
    public boolean isGranted(Collection<? extends GrantedAuthority> authorities, String type, String rank,
            String permission) {
        if (authorities != null) {
            for (GrantedAuthority authority : authorities) {
                if (authority instanceof UserGrantedAuthority userAuthority) {
                    if (this.rolePermissionsConverter != null) {
                        // 从角色清单和许可清单中收集所有许可
                        Set<String> permissions = new HashSet<>();
                        Set<String> roles = userAuthority.getRoles();
                        if (roles != null) {
                            for (String role : roles) {
                                if (StringUtils.isNotBlank(role)) {
                                    Set<String> ps = this.rolePermissionsConverter.getPermissions(role);
                                    if (ps != null) {
                                        permissions.addAll(ps);
                                    }
                                }
                            }
                        }
                        if (userAuthority.getPermissions() != null) {
                            permissions.addAll(userAuthority.getPermissions());
                        }
                        // 用收集的所有许可重新构建用户获权许可对象，用于匹配权限
                        userAuthority = new UserGrantedAuthority(userAuthority.getType(), userAuthority.getRank(),
                                permissions);
                    }
                    if (userAuthority.matches(type, rank, permission)) {
                        return true;
                    }
                }
            }
        }
        return false;
    }

}
